-module(bot_server).
-behaviour(gen_server).
-compile(export_all).

-define(NICKNAME, "SkintBot").
-define(CHANNEL, "#BoterTest").

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

start_link(IrcServer, Port) -> 
    case gen_server:start_link({local, ?MODULE}, ?MODULE, [], []) of
        {ok, _Pid} ->
            start_bot(IrcServer, Port);
        {error, {already_started, OldPid}} ->
            {ok, OldPid};
        Error ->
            error_logger:error_report([{start_link, Error}])
    end.

init([]) -> 
    process_flag(trap_exit, true),
    io:format("~p starting~n", [?MODULE]),
    {ok, 0}.

start_bot(Host, Port) ->
    case gen_server:call(?MODULE, {connect, Host, Port}) of
        {ok, Socket} ->
            io:fwrite(">>>>Starting loop"),
            % Spawn a new process to process loop
            %            spawn(fun() -> loop(Socket) end);
            loop(Socket);
        {error, Reason} ->
            io:fwrite(">>>>Error starting bot: ~p~n", [Reason]);
        Any ->
            io:fwrite(">>>Not Implemented:~p~n",[Any])
    end.

start_loop(Socket) ->
    gen_server:call(?MODULE, {loop, Socket}).

%% --------------------------------------------------------------
%% @spec handle_call
%% @doc events callback implementation
%% --------------------------------------------------------------
handle_call({loop, Socket}, _From, N) ->
    {reply, loop(Socket), N+1};
handle_call({connect, Host, Port}, _From, State) -> 
    io:fwrite("handling connect call"),
    case connect(Host, Port) of
        {ok, Socket} ->
            io:fwrite(">>>>Connected to Server"),
            gen_tcp:send(Socket, "NICK " ++ ?NICKNAME ++  "\r\n"),
            gen_tcp:send(Socket, "USER " ++ ?NICKNAME ++  " * * :New Now Know How\r\n"),
            io:fwrite(">>>>Sending User Information"),
            {reply, {ok, Socket}, State};
        {error, Reason} ->
            {reply, {error, Reason}, State}
    end.

handle_cast(_Msg, State) -> {noreply, State}.
handle_info(Info, State) -> 
    io:fwrite("Info: ~p~n", [Info]),
    {noreply, State}.

terminate(_Reason, _State) ->
    io:fwrite("~p stopping~n", [?MODULE]),    
    ok.
    
code_change(_OldVsn, State, _Extra) -> {ok, State}.

connect(Host, Port) ->
    gen_tcp:connect(Host, Port, [{packet, line}]).

%% --------------------------------------------------------------
%% @spec loop(Socket)
%% @doc Now that we're connected, receive TCP messages and parse them.
%% --------------------------------------------------------------
loop(Socket) ->
    io:fwrite(">>>>Connected Socket is:~p~n", [Socket]),
    receive
        {tcp, Socket, Data} ->
            io:fwrite(">>>>Receiving data"),
            error_logger:info_msg(">>>>Loop: [~w] Received: ~s", [Socket, Data]),
            message_filter(Socket, string:tokens(Data, "#")),
            loop(Socket);
        quit ->
            error_logger:info_msg(">>>>[~w] Received quit message, exiting...~n", [Socket]),
            gen_tcp:close(Socket),
            exit(stopped)
    end.

%% --------------------------------------------------------------
%% @spec handle(message)
%% @doc Parses the data and joins the channel when appropriate
%% Also send the ping response
%% --------------------------------------------------------------
message_filter(_Socket, [_Info, Data]) ->
    io:fwrite("Processing: ~s~n", [Data]);
%handle_query(Socket, string:tokens(Data, ":"));
message_filter(Socket, [Info]) ->
    StrIndex = string:str(Info, "376 " ++ ?NICKNAME),
    io:fwrite(">>> Joining Channel:~s~n", [Info]),
    if
        StrIndex > 0 ->
            join_channel(Socket, ?CHANNEL);
        true ->
            true
    end;    
message_filter(Socket, ["PING"|Rest]) ->
    gen_tcp:send(Socket, "PONG " ++ Rest ++ "\r\n");
message_filter(_, _) ->
    io:fwrite(">>>>default filter"),
    0.

%% --------------------------------------------------------------
%% @spec join_channel(Socket, Channel)
%% @doc sends the instruction to join a channel
%% --------------------------------------------------------------
join_channel(Socket, Channel) ->
    io:fwrite("Joining Channel: ~s~n", [Channel]),
    gen_tcp:send(Socket, "JOIN :" ++ Channel ++ "\r\n").

%% --------------------------------------------------------------
%% @spec irc_channelmsg/2, irc_privmsg/3
%% @doc utility functions to send a response back to channel or a user
%% --------------------------------------------------------------
irc_channelmsg(Socket, Reply) ->
    irc_privmsg(Socket, ?CHANNEL, Reply).

irc_privmsg(Sock, To, Message) ->
    gen_tcp:send(Sock, "PRIVMSG " ++ To ++ " :" ++ Message ++ "\r\n").